############################################################################
# apps/interpreters/python/Makefile
#
# SPDX-License-Identifier: Apache-2.0
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements.  See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.  The
# ASF licenses this file to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance with the
# License.  You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
# License for the specific language governing permissions and limitations
# under the License.
#
############################################################################

include $(APPDIR)/Make.defs

CPYTHON_URL ?= "https://github.com/python/cpython/archive"
CPYTHON_VERSION = $(patsubst "%",%,$(strip $(CONFIG_INTERPRETER_CPYTHON_VERSION)))
CPYTHON_VERSION_MINOR=$(basename $(CPYTHON_VERSION))
CPYTHON_ZIP = v$(CPYTHON_VERSION).zip

CPYTHON_UNPACKNAME = Python
UNPACK ?= unzip -q -o

MACHDEP=nuttx
CONFIG_SITE=${CURDIR}/config.site
SETUP_LOCAL=${CURDIR}/Setup.local
CPYTHON_PATH=$(CURDIR)/$(CPYTHON_UNPACKNAME)

BUILDIR=$(CURDIR)/build
INSTALLDIR=$(CURDIR)/install
HOSTBUILD=$(BUILDIR)/host
HOSTINSTALL=$(INSTALLDIR)/host
HOSTPYTHON=$(HOSTINSTALL)/bin/python3
TARGETBUILD=$(BUILDIR)/target
TARGETINSTALL=$(INSTALLDIR)/target
TARGETLIBPYTHON=$(TARGETINSTALL)/libpython$(CPYTHON_VERSION_MINOR).a
TARGETMODULESPACK=$(TARGETBUILD)/lib/python$(shell echo $(CPYTHON_VERSION_MINOR) | tr -d .).zip
TARGETMODULES=$(TARGETINSTALL)/lib/

CFLAGS += ${INCDIR_PREFIX}$(CPYTHON_PATH)$(DELIM)Include
CFLAGS += ${INCDIR_PREFIX}$(CPYTHON_PATH)$(DELIM)Test
CFLAGS += ${INCDIR_PREFIX}$(CPYTHON_PATH)$(DELIM)Include$(DELIM)internal
CFLAGS += ${INCDIR_PREFIX}$(APPDIR)$(DELIM)system
CFLAGS += ${INCDIR_PREFIX}$(APPDIR)$(DELIM)system$(DELIM)zlib$(DELIM)zlib
CFLAGS += ${INCDIR_PREFIX}$(TARGETBUILD)
CFLAGS += -Wno-shadow -Wno-undef -Wno-format -Wno-builtin-macro-redefined
CFLAGS += -Wno-type-limits -Wno-implicit-fallthrough -Wno-char-subscripts
CFLAGS += -Wno-sign-compare -Wno-unused-const-variable -Wno-unused-function
CFLAGS += -Wno-unused-variable -Wno-overflow -Wno-unused-but-set-variable
CFLAGS += -Wno-strict-prototypes

DEPPATH += --dep-path $(CPYTHON_UNPACKNAME)$(DELIM)Programs
VPATH   += :$(CPYTHON_UNPACKNAME)$(DELIM)Programs

$(CPYTHON_ZIP):
	@echo "Downloading: $(CPYTHON_URL)/$(CPYTHON_ZIP)"
	$(Q) $(call DOWNLOAD,$(CPYTHON_URL),$(CPYTHON_ZIP))

$(CPYTHON_UNPACKNAME): $(CPYTHON_ZIP)
	@echo "Unpacking: $(CPYTHON_ZIP) -> $(CPYTHON_UNPACKNAME)"
	$(Q) $(UNPACK) $(CPYTHON_ZIP)
	$(Q) mv	cpython-$(CPYTHON_VERSION) $(CPYTHON_UNPACKNAME)
	@echo "Patching $(CPYTHON_UNPACKNAME)"
	$(Q) patch -p1 -d $(CPYTHON_UNPACKNAME) < patch$(DELIM)0001-workaround-newlib-resource.h-limitations.patch
	$(Q) patch -p1 -d $(CPYTHON_UNPACKNAME) < patch$(DELIM)0002-fix-various-uint32_t-unsigned-int-type-mismatch-issu.patch
	$(Q) patch -p1 -d $(CPYTHON_UNPACKNAME) < patch$(DELIM)0003-reuse-wasm_assets.py-for-generating-an-archive-of-py.patch
	$(Q) patch -p1 -d $(CPYTHON_UNPACKNAME) < patch$(DELIM)0004-recognize-nuttx-as-a-supported-OS.patch
	$(Q) patch -p1 -d $(CPYTHON_UNPACKNAME) < patch$(DELIM)0005-gh-122907-Fix-Builds-Without-HAVE_DYNAMIC_LOADING-Se.patch
	$(Q) patch -p1 -d $(CPYTHON_UNPACKNAME) < patch$(DELIM)0006-change-var-name-to-avoid-conflict-with-nuttx-unused_.patch
	$(Q) patch -p1 -d $(CPYTHON_UNPACKNAME) < patch$(DELIM)0007-undef-atexit_register.patch
	$(Q) patch -p1 -d $(CPYTHON_UNPACKNAME) < patch$(DELIM)0008-declare-struct-timeval.patch
	$(Q) patch -p1 -d $(CPYTHON_UNPACKNAME) < patch$(DELIM)0009-include-nuttx-sys-select-header-to-define-FD_SETSIZE.patch
	$(Q) patch -p1 -d $(CPYTHON_UNPACKNAME) < patch$(DELIM)0010-check-for-the-d_ino-member-of-the-structure-dirent.patch
	$(Q) patch -p1 -d $(CPYTHON_UNPACKNAME) < patch$(DELIM)0011-avoid-redefinition-warning-if-UNUSED-is-already-defi.patch
	$(Q) patch -p1 -d $(CPYTHON_UNPACKNAME) < patch$(DELIM)0012-hack-place-_PyRuntime-structure-into-PSRAM-bss-regio.patch
	$(Q) patch -p1 -d $(CPYTHON_UNPACKNAME) < patch$(DELIM)0013-transform-functions-used-by-NuttX-to-lowercase.patch

$(HOSTPYTHON):
	mkdir -p $(HOSTBUILD)
	mkdir -p $(HOSTINSTALL)
	$(Q) ( \
			cd $(HOSTBUILD) && $(CPYTHON_PATH)/configure \
			 --with-pydebug \
			 --prefix=$(HOSTINSTALL) \
		 )
	$(MAKE) -C $(HOSTBUILD) install

# The `config.site` file contains settings that override the configuration
# settings provided by the `configure` script. Depending on the features
# enabled on NuttX, this file may need to be adjusted.

$(CONFIG_SITE):
	$(Q) ( cp $(CONFIG_SITE).in $(CONFIG_SITE))
ifeq ($(CONFIG_ARCH_HAVE_FORK),y)
	@echo "export ac_cv_func_fork=\"yes\"" >> $@
else
	@echo "export ac_cv_func_fork=\"no\"" >> $@
endif
ifeq ($(CONFIG_SYSTEM_SYSTEM),y)
	@echo "export ac_cv_func_system=\"yes\"" >> $@
else
	@echo "export ac_cv_func_system=\"no\"" >> $@
endif

# The `Setup.local` file enables or disables Python modules.
# Depending on the features enabled on NuttX, this file may need to be
# adjusted. Please note that the base `Setup.local.in` file  only contains
# a section to disable Python modules. Inserting lines to it will disable
# such modules.

$(SETUP_LOCAL):
	$(Q) ( cp $(SETUP_LOCAL).in $(SETUP_LOCAL))
ifneq ($(CONFIG_ARCH_HAVE_FORK),y)
	@echo "_posixsubprocess" >> $@
endif

# For the Python's `configure` script, please consider the following
# when building for NuttX:
#
# Use sed to remove optimization flags from NuttX's CFLAGS because
# Python's configure script requires them in OPT. Having the flags in
# both places causes a conflict.
#
# Also, use -O0 for OPT because -Os is causing problems in
# Python/Modules/getpath.c (issue will be filed soon to track this
# problem).

$(TARGETBUILD)/Makefile: $(HOSTPYTHON) $(CONFIG_SITE) $(SETUP_LOCAL)
	$(Q) mkdir -p $(TARGETBUILD)/Modules
	$(Q) mkdir -p $(TARGETMODULES)/python$(CPYTHON_VERSION_MINOR)
	$(Q) ( cp Setup.local $(TARGETBUILD)/Modules/Setup.local )
	$(Q) ( \
			cd $(TARGETBUILD); \
			CFLAGS="$(CFLAGS)"; \
			ARCH=$(CONFIG_ARCH); \
			ARCH_CHIP=$(CONFIG_ARCH_CHIP); \
			ARCH="$${ARCH//-/}"; \
			ARCH_CHIP="$${ARCH_CHIP//-/}"; \
			CFLAGS="$$(echo "$${CFLAGS}" | sed 's/-Os //')" \
			CC="$(CC)" \
			CXX="$(CXX)" \
			AR="$(AR)" \
			ARFLAGS=" " \
			MACHDEP="$(MACHDEP)" \
			OPT="-g -O0 -Wall" \
			CONFIG_SITE="$(CONFIG_SITE)" \
			$(CPYTHON_PATH)/configure \
			 --prefix=${TARGETINSTALL} \
			 --disable-shared \
			 --host=$${ARCH}-$${ARCH_CHIP}-nuttx \
			 --build=$(shell $(CPYTHON_PATH)/config.guess) \
			 --with-build-python=${HOSTPYTHON} \
			 --without-mimalloc \
			 --without-pymalloc \
			 --disable-test-modules \
		)

$(TARGETLIBPYTHON): $(TARGETBUILD)/Makefile
	$(MAKE) -C $(TARGETBUILD) regen-frozen
	$(MAKE) -C $(TARGETBUILD) libpython$(CPYTHON_VERSION_MINOR).a wasm_stdlib
	$(Q) ( cp $(TARGETBUILD)/libpython$(CPYTHON_VERSION_MINOR).a $(TARGETLIBPYTHON) )
	$(Q) $(UNPACK) $(TARGETMODULESPACK) -d $(TARGETMODULES)/python$(CPYTHON_VERSION_MINOR)

MODULE    = $(CONFIG_INTERPRETER_CPYTHON)

PROGNAME  += $(CONFIG_INTERPRETER_CPYTHON_PROGNAME)
PRIORITY  += $(CONFIG_INTERPRETER_CPYTHON_PRIORITY)
STACKSIZE += $(CONFIG_INTERPRETER_CPYTHON_STACKSIZE)

MAINSRC += python_wrapper.c

checkgenromfs:
	@genromfs -h 1>/dev/null 2>&1 || { \
 echo "Host executable genromfs not available in PATH"; \
 echo "You may need to download in from https://romfs.sourceforge.net/"; \
 exit 1; \
	}

romfs_cpython_modules.img : $(TARGETLIBPYTHON) checkgenromfs
	@genromfs -f $@ -d $(TARGETMODULES) -V "ROMFS_Test" || { echo "genromfs failed" ; exit 1 ; }

romfs_cpython_modules.h : romfs_cpython_modules.img
	@xxd -i $< | sed -e "s/^unsigned/static const unsigned/g" >$@ || { echo "xxd of $< failed" ; exit 1 ; }

context:: $(CPYTHON_UNPACKNAME)

depend:: romfs_cpython_modules.h

distclean::
	$(call DELDIR, $(BUILDIR))
	$(call DELDIR, $(INSTALLDIR))
	$(call DELDIR, $(CPYTHON_UNPACKNAME))
	$(call DELFILE, $(CPYTHON_ZIP))
	$(call DELFILE, romfs_cpython_modules.img)
	$(call DELFILE, romfs_cpython_modules.h)
	$(call DELFILE, config.site)
	$(call DELFILE, Setup.local)

include $(APPDIR)/Application.mk
